home *** CD-ROM | disk | FTP | other *** search
- Data Validation Techniques
- In Turbo Vision
-
-
- by Danny Thorpe
-
- presented at the
- Borland Languages Conference
- April 1991
-
-
- Data Validation Techniques in Turbo Vision
- Turbo Vision, the new object-oriented event-driven application
- framework introduced in Borland's Turbo Pascal 6.0, has generated quite
- a bit of excitement in programming circles. With Turbo Vision,
- programmers finally have the tools and application framework necessary
- to create programs that can truly benefit from the OOP methodology.
- Programmers benefit from the variety of objects in Turbo Vision that can
- be molded and extended into a multitude of application-specific dialog
- boxes, windows, and other views.
- Turbo Vision is a multi-faceted environment. At a superficial
- level, it is a library of interrelated objects for managing the screen
- display. At another level, though, Turbo Vision is also an application
- framework which defines a particular model of how a program should work,
- both internally and visually. It is this model and its framework
- embodiment that provides the glue which ties the objects in the library
- together into a functional society of objects. A large portion of the
- increased programmer productivity that can be realized with Turbo Vision
- is due to this model as well: the application framework you inherit
- takes care of the actual mechanics of the program, so the programmer has
- just to plug in modules and create cross-connections between objects.
- While many programmers appear to accept the code changes necessary
- to move to Turbo Vision, some are reluctant to also embrace the
- programming model that is an important part of Turbo Vision. Without
- this model, one can wind up using Turbo Vision objects in ways that
- work against the grain of the programming model, creating unnecessary
- complexity and frustration for the programmer.
- A large portion of any program involves feeding in data for the
- program to operate on. Traditional techniques for interactive data
- entry are particularly difficult for programmers to give up when
- learning Turbo Vision. This paper will discuss some of the philosophy
- behind Turbo Vision, the mechanics of various internal operations in
- Turbo Vision, and why some of the old data entry techniques won't work
- anymore. Alternate approaches to data entry and validation more in line
- with Turbo Vision model will be introduced and demonstrated.
-
-
- The Turbo Vision Model
- The Turbo Vision user interface is based upon the IBM System
- Application Architecture, Common User Access specification. CUA
- provides guidelines that determined the behavior of the mouse, input
- lines, push-buttons, and the choice of keys for particular actions, such
- as Esc to cancel a dialog and Enter to select the default button of the
- dialog.
- A driving force in the Turbo Vision programming model is the idea
- that the user of the program should be in control of the program at all
- times. Let the user do what he wants to do, within reason. This
- doesn't sound very remarkable at first, but when you consider how few
- programs on the market allow the user this freedom, you begin to realize
- that an unnecessary evil has been lurking in computer software: too many
- restrictions are imposed upon the user by the program/programmer, to the
- point that the software is in control of the user.
- Of course, a program needs structure in order to organize its
- information into a meaningful form. The user needs the information to
- be structured, too, in order to understand the material. The difficulty
- comes in finding the fine line between the amount of structure needed by
- the user and the amount of structure that begins to stifle the user with
- unnecessary conditions and requirements. Programs frequently wind up on
- the stifling end of the scale simply because they're easier to write and
- manage. With traditional programming tools it is difficult to construct
- a well-balanced, flexible program that considers all contingencies the
- user may wish to embark upon.
- With Turbo Vision, you inherit a well-balanced, flexible program
- architecture and then fill in the details of what your application is to
- do. At the lowest levels in the object hierarchy, Turbo Vision views
- have the ability to be manipulated by the user in a multitude of ways.
- But the programmer doesn't have to bend over backwards in Turbo Vision
- to accomplish this user freedom. All the programmer has to do is work
- within the Turbo Vision model to extend the behavior of view objects
- that already know how to interact with the user.
- The Turbo Vision model provides an environment based on autonomy
- and independence. The careful integration of OOP and event driven
- programming provides the programmer with a rich set of tools and opens
- doors to programming solutions nearly unattainable by traditional
- methods. Working in this model actually provides the programmer more
- flexibility in expression and more opportunity to bring sophisticated
- applications to completion than traditional programming tools can offer.
-
-
-
- Traditional Validation Techniques
- As-You-Type Validation
- One method of ensuring an input field contains "clean" data is to
- monitor each keystroke the user types. Keys can be checked by position
- within the input field if the validation is controlled by a template.
- Formatting telephone number fields with a template like (999) 999-9999,
- where '9' means 'accept any number in this position' is a common use of
- as-you-type validation. If a non-numeric key is pressed, the key
- monitor can beep or provide some other non-intrusive indicator of an
- error. The other characters in the template can be treated just as
- place-holders, so the user doesn't have to enter the parentheses for
- every phone number, for example.
- As-you-type validation is good for position-sensitive formatting
- of data, such as telephone numbers or dates, and for character
- conversions, such as converting all characters entered in a field to
- uppercase as they are typed. As-you-type is difficult to use when
- numbers or whole words need to be validated before being accepted,
- because you don't really know what the user intends until the user is
- finished typing, backspacing, and correcting misspellings.
- Templates are easy to implement in Turbo Vision: make a
- descendent of a TInputLine object and extend the HandleEvent method to
- test each keystroke received by that view. Acceptable keystrokes can be
- passed to the inherited HandleEvent for default processing, and
- unacceptable keys can be discarded with a beep and/or non-intrusive
- error message.
- As-You-Leave Validation
- Another opportunity to test the data is when the user moves the
- cursor out of the field, presumably to the next field on the screen.
- This method assumes that when the user hits the Tab key or Enter key to
- move to the next field, the user is finished keying this field, and so
- it is fair game for validation. Validation is performed before the
- program moves focus to the next field.
- If the contents of the field are unacceptable, it is common for an
- error message to be displayed, and the user is put back in the bad field
- to reenter it. This is where a poorly designed data entry system can be
- really mean to the user - what if the user decides to cancel the whole
- data entry operation? It would be the job of every field to watch for a
- particular "cancel" key, such as F1 or Esc, and shut down the data entry
- mode when such a keystroke occurs.
- Data can be tested as the user leaves a field in Turbo Vision.
- However, the traditional response of forcing the user back to a field
- after the user has indicated he wants to go elsewhere conflicts with the
- Turbo Vision model. Consequently, this behavior is difficult to
- implement in Turbo Vision, mainly because it requires different
- assumptions about the user and environment than Turbo Vision assumes. A
- detailed explanation of the focus change mechanism is covered in the
- next section.
- As-you-leave validation assumes that when the user leaves the
- field, the field is ready to be validated. This is a safe assumption in
- a single-window keyboard-only program. But Turbo Vision's easy access
- to multi-windowed text and mouse support invalidates this assumption,
- because the user could be leaving the field to go to an entirely
- different window, such as a text editor or calculator.
- Assume for a moment that a programmer has implemented an As-you-
- leave, force-back-if-invalid validation scheme for Turbo Vision views.
- Picture a dialog box with a few data entry lines, a close-icon on the
- frame, an Ok button, and a Cancel button. The user enters invalid data
- in one of the input fields and presses the tab key to move to the next
- input field. The validation routines kick in when the current field is
- about to lose focus, detect the invalid entry, and prevent the user from
- moving to the next input field until valid data is entered. A mild
- frustration for the user, perhaps, but a liveable situation.
- Let's say the user enters invalid data in the input field, then
- clicks the mouse on the Cancel button in the dialog. This will cause a
- focus change from the current input field to the dialog button. Since
- controls in a dialog usually operate completely oblivious of each other,
- the input field has no way of knowing where the focus is going to when
- it loses focus. In this scenario, the validation routines would still
- force the user back to the input field until the field contained valid
- data. What's the point of having a Cancel button if you can't use it
- when you need it most? This is an unacceptable situation.
- For the sake of argument, let's say the programmer recodes all the
- input field objects with throwback validation to recognize a focus
- change to a special Cancel button and allows the cancel to take place.
- The user is appeased for the moment, but what happens when the user
- demands a new button on that dialog? More special casing, more hidden
- interdependencies?
- The programmer has committed one of the worst sins of object-
- oriented programming - special casing in general purpose objects - which
- quickly defeats many of the advantages of OOP: independence,
- flexibility, maintainability. As soon as the user demands a new button
- on that dialog, the programmer must recode all the input field objects
- related to that throwback validation scheme, expending more time and
- energy on an old problem, possibly destabilizing the entire application,
- and never really solving the problem to begin with. All this, just to
- add a simple button.
- As-you-leave validation is not improper in Turbo Vision, if it is
- done non-intrusively. A simple As-you-leave validation response to bad
- data is to simply change the color of the field. The user will see the
- possible error and can address the problem as he sees fit.
- When-You're-Done Validation
- A third opportunity to test the data is when the screen or record
- is complete and the user indicates he is finished entering or editing
- the data. All forms of validation can be performed at this stage
- because the data is complete and the user has indicated the data is
- complete. No assumptions to tippy-toe around here. If one of the
- fields contains unacceptable data, the user can be returned to the bad
- field with an error message. The problem of providing the user an
- escape still exists.
- When-you're-done validation is Turbo Vision's default validation
- mode. When the user exits a dialog, the Valid method of every view in
- the dialog is called. If any Valid method returns False, then the
- dialog will not close. If the user cancels the dialog, no validation
- calls are made and the data in the dialog is discarded.
- This data validation method is the most flexible in Turbo Vision.
- One can extend the behavior of TDialog, for example, to perform data
- validation and respond to errors in a variety of ways. As detailed
- later in this paper, a dialog can be made to find which view is invalid
- and return the user to that view for corrections. And by validating the
- data when the user indicates he's ready, we avoid all the problems
- associated with As-you-leave validation in a multi-windowed environment.
-
-
- Focus Change Internals
- When a user hits the Tab key to move from one dialog control to
- another, a rapid sequence of events and object method calls occurs to
- transfer the focus of the dialog from one view to another. Programmers
- tend to attack this focus change sequence when attempting to force Turbo
- Vision to return focus to a bad field in an As-you-leave data
- validation. This section describes the sequence of events in a focus
- change and why it should not be aborted in mid-stream.
- Turbo Vision terms will be flying fast and furious in a moment.
- If you are not familiar with the terms view, group, focused, and
- selected, take a minute to review the terminology appendix at the end of
- this paper.
- Press the Tab key.
- Let's say the user is looking at a dialog which has two input
- lines. When the user presses the Tab key, an evKeydown event is
- generated, which is sent to the dialog's HandleEvent method. The Tab
- key event is passed back to ancestral handleevents and eventually gets
- processed by TWindow.HandleEvent. For a Tab key, TWindow.HandleEvent
- calls TGroup.SelectNext.
- TGroup.SelectNext scans through the group's (dialog's) list of
- views for the next selectable view. In this example, the next
- selectable view is the second input line. The second input line's
- Select method is called, which by inheritance works out to be
- TView.Select.
- TView.Select calls TGroup.SetCurrent to make itself (the second
- input line) the current view.
- TGroup.SetCurrent
- The TGroup object contains a private method called SetCurrent that
- performs the dirty work of changing the focus from one view to another.
- It is a private method because the operations it performs are low-level
- and are not considered safe to modify.
- TGroup.SetCurrent does four things of importance, and all of them
- involve setting the state flags of the two views transferring focus.
- First, the currently focused view is de-focused, then de-selected.
- Next, the new view to receive focus is selected, then focused.
- We'll trace what happens when a view changes state in a moment.
- Right now, it's important to observe that there is no room for
- arbitration in this rapid fire sequence of defocusing and refocusing.
- View A is told to de-focus and de-select itself. Then View B is told to
- select and focus itself. If a programmer were to modify a view's
- SetState method to refuse to de-focus itself when told to, the group
- would be corrupted with what would appear to be two focused views, even
- though only one view can be focused at a time.
- The group assumes that state changes (like from focused to not
- focused) will be successful. This is in sync with the Turbo Vision
- program model, which assumes that you can always switch between non-
- modal views in an application. This also greatly simplifies the logic
- of focus changes and other operations that change a view's state,
- because we don't have to worry about how to "back up" if a state change
- failed late in a focus change sequence.
- Tracing the SetStates
- Let's call the first input line which is losing focus View A, and
- the input line that's receiving focus View B. TGroup.SetCurrent calls
- ViewA^.SetState(sfFocused, False). TView.SetState sends a
- cmReleasedFocus broadcast event to the view's owner (the dialog) via the
- Message function. The Message function takes an event as parameters and
- pipes it directly into the destination object's HandleEvent method.
- The dialog's HandleEvent receives a cmReleasedFocus broadcast
- event. By default, TDialog doesn't do anything with the event, and none
- of the ancestral HandleEvents do anything with it either. This message
- is mainly to inform whoever might care that the view is releasing focus.
- When the event has run its course through the dialog's subviews, the
- Message function will return control to the TView.SetState which called
- it.
- Control returns to TGroup.SetCurrent, which next calls
- ViewA^.SetState(sfSelected, False). TView.SetState simply clears the
- sfSelected flag in its State field, but some TView descendents (such as
- TButton) may redraw themselves to reflect different colors for selected
- vs non selected states.
- Control returns to SetCurrent, which next calls
- ViewB^.SetState(sfSelected, True). The sfSelected flag is set in ViewB,
- which may cause a redraw to reflect colors of the new state.
- Control returns to SetCurrent, which last calls
- ViewB^.SetState(sfFocused, True). TView.SetState sends a
- cmReceivedFocus broadcast event to its owner-dialog via the Message
- function. The dialog receives the event in its HandleEvent, but like
- the cmReleasedFocus broadcast, this broadcast event is mainly for
- informational purposes and is not used by any of the inherited
- HandleEvents.
- When control again returns to SetCurrent, the group's Current
- pointer is set to point to ViewB. The focus change sequence is
- complete.
- Summary
- Turbo Vision assumes that state changes, particularly selection
- and focus state changes, will succeed when invoked. This section has
- stepped through a focus change sequence to illustrate who all the
- players are behind the scenes and show the roles they play. Attempting
- to interrupt or short-circuit a focus change will leave the dialog in an
- unstable, probably unusable state.
-
-
-
- Demo Programs
- So you don't go away empty handed, here are two very simple demo
- programs that illustrate how the interplay between certain TView and
- TGroup methods can be used to perform data validation. These examples
- are primarily to show where you can plug in validation code - not how to
- validate your data.
- As-You-Leave Validation
- The Valid1.pas program on the disk accompanying this paper
- implements data validation code in a TInputLine descendent that checks
- the data when the input line loses focus. If the data is unacceptable,
- the input line is redrawn in red. It will get the user's attention, but
- will not interfere with what he is doing.
- TValidInputLine has three main parts, and all of them are very
- small: a Valid function to test the data, a SetState procedure to call
- Valid when the view loses focus, and a GetPalette function to map an
- error color (flashing white on red) into the standard TInputline color
- palette when the input line is in an error condition. All these methods
- are extending inherited ancestor methods and behaviors. TValidInputLine
- also has a new boolean field, IsValid, to keep track of what the result
- of the last Valid check was and to determine whether GetPalette returns
- an error color palette or its standard color palette. As soon as the
- error color is no longer needed or wanted, IsValid is set to True, which
- will cause the standard color palette to be used the next time the view
- is drawn.
- TValidInputLine looks like this:
- type
- TValidInputLine = object(TInputLine)
- IsValid: Boolean;
- constructor Init(var Bounds: TRect;
- AMaxLen:Integer);
- function GetPalette: PPalette; virtual;
- procedure SetState(AState: Word;
- Enable: Boolean); virtual;
- function Valid(Command: Word): Boolean; virtual;
- end;
- constructor TValidInputLine.Init(var Bounds: TRect;
- AMaxLen: Integer);
- begin
- TInputLine.Init(Bounds, AMaxLen);
- IsValid := Valid(cmOk);
- end;
- function TValidInputLine.GetPalette: PPalette;
- const AltPalette: String[Length(CInputLine)]
- = CInputLine;
- begin
- AltPalette[1] := #255;
- if IsValid then
- GetPalette := TInputLine.GetPalette
- else
- GetPalette := @AltPalette;
- end;
- procedure TValidInputLine.SetState(AState: Word;
- Enable: Boolean);
- begin
- if ((AState and sfFocused) <> 0) and
- GetState(sfFocused) then
- Valid(cmOk);
- TInputLine.SetState(AState, Enable);
- end;
- function TValidInputLine.Valid(Command: Word): Boolean;
- begin
- if Command <> cmCancel then
- begin
- IsValid := (Data^ = 'Hello');
- Valid := IsValid;
- Write(#7); { beep }
- DrawView; { Show new colors }
- end;
- end;
- The Init constructor initializes IsValid by calling the Valid
- method. Whenever the view loses focus, the SetState method calls Valid,
- which checks the string data of the input line against the only valid
- response - 'Hello'. IsValid is set and DrawView is called to display
- the new colors. DrawView will use GetPalette in the process of
- redisplaying the input line, and GetPalette knows to check IsValid to
- see which palette should be returned.
- Compile and run the simple shell program and dialog that is
- Valid1.pas. You will notice the input line is immediately red, because
- the Init constructor calls Valid and the string that's in the input line
- is 'Phone home.' not the 'Hello' that Valid is looking for. When you
- begin to type, the input line returns to its normal colors. When you
- Tab to the Ok button, Valid is called and the error color may be
- redisplayed, depending upon what you typed.
- Selecting the Cancel button, pressing Escape, or clicking on the
- close icon of the dialog will all cancel the dialog with no further
- validation. Selecting the Ok button or pressing Enter will close the
- dialog with a cmOk command, and a final round of validation is done
- before the dialog closes. If the input line is not valid, the dialog
- will not close. The user must either correct the data or cancel the
- dialog.
- When-You're-Done Validation
- Turbo Vision dialogs already verify that all their controls return
- Valid true before the dialog is allowed to close. Valid2.pas selects
- the first control that returned Valid false, so the user is placed on
- the control with bad data. Valid2.pas also displays the control in the
- red error color.
- Valid2.pas uses a similar TValidInputLine object: the only things
- that are different in Valid2 are that the SetState method was dropped
- and a HandleEvent method was added, and the DrawView call was removed
- from the Valid method.
- Valid2 adds a new dialog object type, TDemoDialog, which has
- simply a new Valid method which overrides the inherited TDialog.Valid
- method. This new method finds the first control whose Valid method
- returns false, and selects that control to make it the currently focused
- view.
- Compile and run Valid2.pas. Tab around the dialog a bit, then
- select the Ok button. The dialog beeps as the input line's valid method
- objects to closing, the input line is painted red, and the input line
- becomes the focused view. When you begin typing into the input line or
- Tab to another view, the input line is redisplayed in its normal colors.
-
- End.
- Though extremely simple, these two programs should provide a
- breadboard upon which you can build and test much more elaborate data
- validation routines of your own. Armed with some additional knowledge
- of Turbo Vision's internals, your data validation routines can also work
- with Turbo Vision, instead of against it, for greater longevity of the
- program and programmer.
-
-
-
- Appendix
- Terminology
- Let's review some Turbo Vision terms. A view is any TView object
- or any TView descendent object, including TGroup, TWindow, TDialog,
- TButton, TInputline, etc. The simplest displayable object in Turbo
- Vision is a TView, and everything visible on the screen is managed by an
- object which is in some way descended from TView.
- Similarly, a group is a TGroup or any descendent of a TGroup, such
- as a TWindow or TDialog. A group is a view which maintains a list of
- other views which the group is said to "own." Views are inserted into
- groups, groups maintain a coordinate system relative to the group's
- rectangle origin, and groups distribute events to their subviews in an
- order that's determined by the type of event.
- The focused view is the view which receives focused events like
- keystrokes and command events. There can be only one focused view in an
- application at any time.
- A selected view is the view in a group which the group's Current
- pointer is pointing to. Each group can have only one selected view, but
- since the application can contain many groups, there can be many
- selected views in an application at one time. When a group receives
- focus, it gives the focus to its selected view. When a view loses focus
- to a view in a different group, the view retains its selected state in
- its group but relinquishes its focused state.
- A focused view will always also be selected. However, a view can
- be selected without being focused. Basically, the selected state is a
- place holder for which view in a particular group should receive focus
- when focus returns to that group.
- Most of the behavior of a dialog is inherited from the TGroup
- object type, so many of the methods discussed will be described as
- TGroup methods, not TDialog methods. Understanding what parts of an
- object are inherited, and therefore shared by all descendents of an
- ancestor, is important to get the most out of Turbo Vision. When you
- want a dialog that has a slightly different Execute method, for example,
- its useful to know that the default Execute method is inherited from
- TGroup, but when you actually implement the new Execute method, it will
- be in a TDialog descendent object.
-
-
- Acknowledgements
-
- Chuck Jazdzewski, for guidance.
- Eberhard Waiblinger, for time.
- Kurt Diesch, for questions.
-
-
- 2
-
-
-